﻿#region Opis pliku
//-----------------------------------------------------------------------------
// Unit.cs
// Autor: Jan Fraś
// 30.10.2012
//-----------------------------------------------------------------------------
#endregion

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Content;

namespace pogra
{
    /// <summary>
    /// klasa jednostek przemieszczających się. 
    /// Dostarcza podstawowe metody takie jak ruch, znajdowanie najbliższego przeciwnika i znajdowanie drogi do celu.
    /// </summary>
    /// 
    public abstract class Unit : Entity
    {
        
        #region pola

        public struct Flags
        {
            public bool GoalPositionToReach;
            public bool Attacked;
            public bool Selected;
            public bool Leader;
            public bool BeingDaed;
            public bool RemoveMePlease;
        }
        protected Flags flags = new Flags();

        public enum Moodes
        {
            Aggressive,
            Defensive
        };



        protected enum UnitState
        {
            Idle,
            Patrol,
            Traveling,
            Dead,
            Attacking
        };
        
        protected UnitState State;
        protected float velocity = 0;
        protected float maxvelocity = 0.1f;
        protected float rotationspeed = 0;
        protected Entity enemy;
        protected float range = 1000;
        protected float shotrange = 1000;
        protected float sightrange = 600;
        protected int shotInterval = 1000;
        protected int lastshottime;
        protected int experience = 0;
        protected int rank = 0;
        protected int upgradeQuant;
        protected int attackPower;
        protected int ExperienceLimit = 300;
        int corpsetime = 10000;
        protected Vector2 GoalPosition;
        List<Unit> troop = new List<Unit>();
        protected BulletManager bulletmanager;
        protected Moodes mood = Moodes.Defensive;
        private int formation;

        #endregion

        #region inicjalizacja
        public Unit(Vector2 pos, float rotation, int team, List<Unit> enemyteam)
            : base(pos, rotation, team, enemyteam)
        {
            lastshottime = Rand.next(0, shotInterval);
            State = UnitState.Idle;
            bulletmanager = new BulletManager(particleSystem);
        }
        #endregion

        #region różna logika
        virtual public bool move(TimeSpan deltat)
        {
            position.X += (float)(velocity * Math.Cos(rotation) * deltat.Milliseconds);
            position.Y += (float)(velocity * Math.Sin(rotation) * deltat.Milliseconds);
            rotation += rotationspeed*deltat.Milliseconds;// % (float)Math.PI*2;
            if (rotation < -Math.PI)
                rotation += (float)Math.PI * 2;
            if (rotation > Math.PI )
                rotation = rotation - (float)Math.PI * 2;

            return true;
        }

        virtual protected bool setvalues()
        {
            if (enemy != null)
            {
                float tmp = -rotation + (float)((Math.Atan2(enemy.Position.Y - position.Y, enemy.Position.X - position.X) + Math.PI * 2) % (Math.PI * 2));

                if (tmp > Math.PI)
                    tmp = -(float)Math.PI * 2 + tmp;
                else
                    if (tmp < -Math.PI)
                        tmp = +(float)Math.PI * 2 + tmp;

                rotationspeed = 0.01f * (tmp);

                if ((position - enemy.Position).Length() > range && velocity < maxvelocity)
                {
                    velocity += 0.01f;
                }
                else
                {
                    velocity *= 0.5f;
                    if (Math.Abs(velocity) < maxvelocity / 10f)
                    {
                        velocity = 0;
                    }
                }


                velocity *= (float)Math.Cos(tmp);

            }
            return true;
        }

        public void report(int value)
        {
            experience += value;
            if (experience > ExperienceLimit && rank < 3)
            {
                experience = 0;
                upgrade();
            }
        }

        virtual protected void upgrade()
        {
            rank++;
            attackPower += upgradeQuant;
            shotInterval = (int)(shotInterval * 0.9);
        }

        virtual protected bool setvaluesTravelMode()
        {
            if(GoalPosition != null)
            {
                float tmp = -rotation + (float)((Math.Atan2(GoalPosition.Y - position.Y, GoalPosition.X - position.X) + Math.PI * 2) % (Math.PI * 2));

                if (tmp > Math.PI)
                    tmp = -(float)Math.PI * 2 + tmp;
                else
                    if (tmp < -Math.PI)
                        tmp = +(float)Math.PI * 2 + tmp;

                rotationspeed = 0.01f * (tmp);

                if ((position - GoalPosition).Length() > 10 && velocity < maxvelocity)
                {
                    velocity += 0.01f;
                }
                else
                    if ((position - GoalPosition).Length() < 100)
                    {
                        velocity *= 0.7f;
                        rotationspeed = 0;

                        if (Math.Abs(velocity) < maxvelocity / 10f)
                        {
                            velocity = 0;
                            return true;
                        }
                        else
                            return false;
                    }
                velocity *= (float)Math.Cos(tmp);
                return false;
            }
            return true;
        }

        protected bool findtarget()
        {
            if (enemy != null)
                if (enemy.Health <= 0)
                    enemy = null;

            if (enemy == null)
            {
                float distance = 0;

                /*
                 * generalnie dziala, ale cos się strasznie wiesza jak zaczyna brakowac zywych:
                 */

                //try
                //{
                //    enemy = enemyteam.First(item => item.Health > 0);
                //    distance = (position - enemy.Position).Length();
                //}
                //catch (InvalidOperationException)
                //{
                //    enemy = null;
                //    return false;
                //}

                /* 
                 * dlatego szukamy pierwszego w ten sposób:
                 */
                foreach (Entity a in enemyteam)
                {
                    if(a.Health > 0)
                    {
                        enemy = a;
                        distance = (position - enemy.Position).Length();
                        break;
                    }

                }

                foreach (Entity a in enemyteam)
                {
                    if (((( position - a.Position).Length() < distance) && a.Health > 0))
                    {
                        distance = (position - a.Position).Length();
                        enemy = a;
                    }
                }
                if (mood == Moodes.Aggressive || distance < sightrange)
                { }
                else
                    enemy = null;

            }

            return (enemy != null);
        }

        public void settarget(Unit a)
        {
            enemy = a;
            mood = Moodes.Aggressive;
        }

        public void setGoalPosition(Point point)
        {
            GoalPosition = new Vector2((float)point.X, (float)point.Y);
            flags.GoalPositionToReach = true;
        }

        public void setGoalPosition(Vector2 point)
        {
            setGoalPosition(new Point((int)point.X, (int)point.Y));
        }

        virtual protected bool attack()
        {
            return ((enemy.Position - position).Length() < shotrange && lastshottime < lifetime - shotInterval);
        }

        public void select(bool p)
        {
            if(p == true)
                if (flags.Leader == true)
                {
                   // if (troop.Count > 1)
                        //leaderSet(troop, new Point((int)destination.X, (int)destination.Y));
                    flags.Leader = false;
                }
            flags.Selected = p;
        }

        public void leaderSet(List<Unit> chosen, Point mousePos, int formation)
        {
            if (formation == 0)
            {
                flags.Leader = false;
                return;
            }

            this.formation = formation;

            flags.Leader = true;
            troop.Clear();

            for (int i = 1; i < chosen.Count; i++)
            {
                troop.Add(chosen[i]);
            }

            setGoalPosition(mousePos);
        }

        protected void lead(bool dying = false)
        {
            Vector2 temp = position;
            int j = 1, k = 1;
            float i = 0.5f;


            //jeśli to ostatnia komenda - chrzanić szyk, wyślij wszystkim miejsce docelowe
            if (dying == true)
            {
                temp = GoalPosition;
            }


            switch(formation)
            {

                case 1:
                    {
                        //liniowo
                        foreach (Unit asd in troop)
                        {
                            k = -k;
                            i += 0.5f;
                            j = (int)i * k;
                            asd.setGoalPosition(new Point((int)(position.X + j * Math.Sin(rotation) * 30), (int)(position.Y - j * Math.Cos(rotation) * 30)));
                        }
                        break;
                    }
                case 2:
                    {
                        foreach (Unit asd in troop)
                        {
                            int distancex = 30, distancey = 60;
                            if (j == k)
                            {
                                k++;
                                j = 0;
                            }
                            asd.setGoalPosition(new Point((int)(temp.X + (j - k / 2f + 0.5f) * Math.Sin(rotation) * distancey + (-k + 1) * Math.Cos(rotation) * distancex), (int)(temp.Y - (j - k / 2f + 0.5f) * Math.Cos(rotation) * distancey + (-k + 1) * Math.Sin(rotation) * distancex)));
                            j++;
                        }
                        break;
                    }
            }

        }

        public override int takehit(int hit, float attackrotation)
        {
            flags.Attacked = true;
            return base.takehit(hit, attackrotation);
        } 
        #endregion

        #region metody stanów
        protected virtual void IdleState(TimeSpan deltat)
        {
            if (flags.GoalPositionToReach == true)
            {
                State = UnitState.Traveling;
            }
        }

        virtual protected void TravelingState(TimeSpan deltat)
        {
            if (setvaluesTravelMode() == true)
            {
                flags.GoalPositionToReach = false;
                State = UnitState.Idle;
                return;
            }
            move(deltat);

            if (flags.Leader == true)
                lead();
            if (flags.Attacked == true)
                findtarget();
        }

        virtual protected void AttackingState(TimeSpan deltat)
        {
            if (flags.GoalPositionToReach == true)
            {
                State = UnitState.Traveling;
            }
            
            if (enemy.Health <= 0)
            {
                State = UnitState.Idle;
            }
        }

        virtual protected void DeadState(TimeSpan deltat)
        {
            if (flags.BeingDaed == false)
            {
                //particleSystem.AddEmitter(new ExplosionEmitter(position));
                flags.BeingDaed = true;
                lifetime = 0;
            }
                
            if (flags.Leader == true)
            {
                lead(true);
                flags.Leader = false;
            }

            if (lifetime > corpsetime)
                flags.RemoveMePlease = true;

            color = Color.Black * (float)((float)(corpsetime - lifetime) / corpsetime);
        }

        virtual protected void PatrolState(TimeSpan deltat)
        {
        }
        #endregion

        #region metody draw i update
        public override bool Update(TimeSpan deltat)
        {

            switch (State)
            {
                case UnitState.Idle:
                    IdleState(deltat);
                    break;

                case UnitState.Traveling:
                    TravelingState(deltat);
                    break;

                case UnitState.Attacking:
                    AttackingState(deltat);
                    break;

                case UnitState.Dead:
                    DeadState(deltat);
                    break;

                case UnitState.Patrol:
                    PatrolState(deltat);
                    break;

                default:
                    IdleState(deltat);
                    break;
            }

            base.Update(deltat);
            bulletmanager.Update(deltat);
                
            if(hp<=0)
                State = UnitState.Dead;

            return true;

        }
        public override void Draw(ExtendedSpriteBatch exSpriteBatch)
        {
            if (Health > 0)
            {
                if (flags.Selected == true)
                    color = Color.DeepSkyBlue;
                else
                    color = Color.Gray;

                for (int i = 0; i < rank; i++)
                {
                    int size = 4;
                    int pos = 10;
                    exSpriteBatch.DrawLine(position + new Vector2(pos, -pos + i * size), position + new Vector2(pos + size, -pos - size + i * size), Color.Yellow);
                    exSpriteBatch.DrawLine(position + new Vector2(pos + size * 2 + 1, -pos + i * size + 1), position + new Vector2(pos + size, -pos - size + i * size), Color.Yellow);
                    
                }
                if (flags.GoalPositionToReach)
                    exSpriteBatch.DrawCircle(GoalPosition, 5, 1, Color.Lime);
                if(enemy != null && team == 1)
                    exSpriteBatch.DrawLine(position, enemy.Position, Color.Red * 0.2f, 1);
            }

            bulletmanager.Draw(exSpriteBatch);
            base.Draw(exSpriteBatch);

        }
        #endregion

        #region właściwosci

        public Moodes Mood
        {
            set { mood = value; }
            get { return mood; }
        }

        public Flags flag
        {
            get { return flags; }
        }
        #endregion


    }
}
